home *** CD-ROM | disk | FTP | other *** search
- /*
- ** NEWS2MAIL
- ** Read a news article on standard input, and send it to the mailing
- ** list as directed by the command-line arguments. It does some
- ** parsing and converting of news headers into mail headers.
- **
- ** This program wants to lie to sendmail, so it should be setuid to
- ** one of the "trusted" users as listed in your sendmail.cf file.
- **
- */
- #include "gate.h"
- #include <sys/stat.h>
- #if defined(RCSID)
- static char RCS[] =
- "$Header: /nfs/papaya/u2/rsalz/src/newsgate/RCS/news2mail.c,v 1.14 91/07/18 21:14:26 rsalz Exp $";
- #endif /* defined(RCSID) */
-
-
- /* Flags for special header lines. */
- typedef enum _HEADERTYPE {
- HDR_NORM,
- HDR_SUBJ,
- HDR_CTRL,
- HDR_REFS,
- HDR_PATH,
- HDR_FROM
- } HEADERTYPE;
-
-
- /* Header-cracking datatype. */
- typedef struct _HEADER {
- char *Tag;
- int Length;
- HEADERTYPE Flag;
- char Value[SM_SIZE];
- } HEADER;
-
- STATIC int Debugging;
- char *Pname;
-
- /* The headers we care about. */
- STATIC HEADER Table[] = {
- { "Control", 7, HDR_CTRL },
- { "Date", 4, HDR_NORM },
- { "From", 4, HDR_FROM },
- { "Message-ID", 10, HDR_NORM },
- { "Organization", 12, HDR_NORM },
- { "Path", 4, HDR_PATH },
- { "References", 10, HDR_REFS },
- { "Reply-To", 8, HDR_NORM },
- { "Subject", 7, HDR_SUBJ },
- };
-
-
- #if defined(USE_PATH_FOR_FROM)
- /*
- ** Figure out the return address, by doing some optimization on the
- ** path. If we find an Internet host or a UUCP neighbor, remove all
- ** hosts before that one from the path.
- */
- STATIC char *
- EditPath(path, Host)
- register char *path;
- char *Host;
- {
- static char **Hinet;
- static char **Huucp;
- static char **Lsys;
- static char buff[SM_SIZE];
- register char *p;
- register char *q;
- register char **V;
- register int ac;
- register int i;
- register int uucppal;
- register int inetpal;
- FILE *F;
- FILE *P;
- struct stat Sb1;
- struct stat Sb2;
- char **av;
- char *Inetsite;
-
- if ((ac = Split(path, &av, '!')) == 2) {
- /* Simple case: "site!user" */
- Strcpy(buff, av[1]);
- SplitFree(&av);
- return buff;
- }
-
- /* Initialize. This is silly for now, but eventually we might want
- * to be able to handle a batched feed. */
- #if defined(UUNAME)
- if (Lsys == NULL) {
- p = UUNAME;
- /* If someone diddled L.sys, rebuild uuname output. */
- if (stat(p, &Sb2) < 0
- #if defined(L_SYS)
- || stat(L_SYS, &Sb1) < 0
- #endif /* defined(L_SYS) */
- || Sb1.st_mtime >= Sb2.st_mtime)
- if ((F = fopen(p, "w")) == NULL)
- Fprintf(stderr, "%s: Can't create %s, %s.\n",
- Pname, p, strerror(errno));
- else {
- if ((P = popen("exec uuname", "r")) == NULL)
- Fprintf(stderr, "%s: popen failed, %s.\n",
- Pname, strerror(errno));
- else {
- while (fgets(buff, sizeof buff, P))
- Fputs(buff, F);
- if (pclose(P))
- Fprintf(stderr, "%s: pclose failed, %s.\n",
- Pname, strerror(errno));
- }
- if (fclose(F) == EOF)
- Fprintf(stderr, "%s: Error closing %s, %s.\n",
- Pname, p, strerror(errno));
- }
-
- /* Slurp up names of UUCP hosts we talk to. */
- Lsys = ReadFile(p);
-
- #if defined(UUCP_INET)
- /* Slurp up the UUCP->Internet name mappings. */
- for (Huucp = ReadFile(UUCP_INET), i = 1; Huucp[i]; i++)
- continue;
- for (Hinet = NEW(char*, i), i = 0; (p = Huucp[i]) != NULL; i++) {
- while (*p && !WHITE(*p))
- p++;
- if (*p)
- for (*p++ = '\0'; *p && WHITE(*p); p++)
- continue;
- Hinet[i] = p;
- }
- #endif /* defined(UUCP_INET) */
- }
- #endif /* defined(UUNAME) */
-
- /* Scan the path, noting if we find a UUCP or Internet neighbor. */
- for (uucppal = 1, inetpal = 0, i = 0; i < ac; i++) {
- if (Lsys)
- for (V = Lsys; *V; V++)
- if (EQ(av[i], *V))
- uucppal = i;
- if (Huucp)
- for (V = Huucp; *V; V++)
- if (EQ(av[i], *V)) {
- inetpal = i;
- Inetsite = Hinet[V - Huucp];
- }
- }
-
- if (inetpal < uucppal) {
- /* No Internet site found, turn a!b!c into a!b!c@this-host */
- for (p = buff + APPEND(buff, av[uucppal]); ++uucppal < ac; ) {
- *p++ = '!';
- p += APPEND(p, av[uucppal]);
- }
- *p++ = '@';
- Strcpy(p, Host);
- }
- else if (inetpal == ac - 1)
- /* Turn a!b!inet!user into user@inet.domain.name */
- Sprintf(buff, "%s@%s", av[ac], Inetsite);
- else {
- /* Turn a!inet!b!user into b!user@inet.domain.name */
- for (p = buff + APPEND(buff, av[++inetpal]); ++inetpal < ac; ) {
- *p++ = '!';
- p += APPEND(p, av[inetpal]);
- }
- *p++ = '@';
- Strcpy(p, Inetsite);
- }
-
- /* Convert all but the last "@" to "%" (fie on decwrl and psuecl!). */
- if ((p = IDX(buff, '@')) != NULL)
- for ( ; (q = IDX(p + 1, '@')) != NULL; p = q)
- *p = '%';
-
- SplitFree(&av);
- return buff;
- }
- #endif /* defined(USE_PATH_FOR_FROM) */
-
-
- /*
- ** Hack up the references, taking only the last three.
- */
- STATIC char *
- TrimReferences(refs)
- char *refs;
- {
- static char buff[SM_SIZE];
- register char *p;
- register int i;
- register int ac;
- char **av;
-
- if ((ac = Split(refs, &av, '\0')) != 0) {
- /* Tricky. If there are five references, we want subscripts 2,3,4. */
- i = ac < 3 ? 0 : ac - 3;
- for (p = buff + APPEND(buff, av[i]); ++i < ac; ) {
- *p++ = ',';
- *p++ = ' ';
- p += APPEND(p, av[i]);
- }
- SplitFree(&av);
- }
- else
- buff[0] = '\0';
- return buff;
- }
-
-
- #if !defined(HAVE_PUTENV)
- /*
- ** A brute-forced implementation of putenv. Wastes memory. Consider
- ** it incentive to install the free BSD version...
- */
- int
- putenv(val)
- char *val;
- {
- char **new;
- int i;
- int length;
- int found;
- char *p;
-
- /* See if the value is already in the environment. */
- found = -1;
- if (p = IDX(val, '=')) {
- for (length = ++p - val, i = 0; environ[i]; i++)
- if (EQn(val, environ[i], length)) {
- found = i;
- break;
- }
- }
-
- /* Get the size, and space for the new environment. */
- for (i = 0; environ[i]; i++)
- ;
- i += 2;
- new = NEW(char*, i);
- new[0] = val;
-
- /* Copy the old to the new. */
- for (i = 0; environ[i]; i++)
- if (i != found)
- new[i + 1] = environ[i];
- new[i + 1] = NULL;
- environ = new;
- return 0;
- }
- #endif /* !defined(HAVE_PUTENV) */
-
-
- /*
- ** Print a usage message and exit.
- */
- STATIC void
- Usage()
- {
- Fprintf(stderr, "Usage:\n\t%s %s %s\n",
- Pname,
- "[-.] [-e var=val]",
- "listname listaddr listadmin host [article]");
- exit(EX_USAGE);
- }
-
-
- main(ac, av)
- int ac;
- register char *av[];
- {
- static char tmp[sizeof TEMPFILE];
- register FILE *F;
- register HEADER *hp;
- register char *p;
- char *sv[10];
- char buff[BUFSIZ];
- char SenderAddr[SM_SIZE];
- char ToAddr[SM_SIZE];
- char Host[SM_SIZE];
- #if defined(USE_PATH_FOR_FROM)
- char Fullname[SM_SIZE];
- #endif /* defined(USE_PATH_FOR_FROM) */
- int i;
- int HadEflag;
- char *Listname;
- char *Listaddr;
- char *Listadmin;
- char *Listhost;
- char *Article;
-
- /* Set defaults. */
- if ((Pname = RDX(av[0], '/')) == NULL)
- Pname = av[0];
- else
- Pname++;
- HadEflag = FALSE;
-
- /* Parse JCL. */
- while ((i = getopt(ac, av, "E:.")) != EOF)
- switch (i) {
- default:
- Usage();
- /* NOTREACHED */
- case '.':
- Debugging = TRUE;
- break;
- case 'E':
- if (putenv(COPY(optarg))) {
- Fprintf(stderr, "%s: Can't add to environment, %s.\n",
- Pname, strerror(errno));
- exit(EX_TEMPFAIL);
- }
- HadEflag = TRUE;
- break;
- }
- ac -= optind;
- av += optind;
- if (ac != 4 && ac != 5)
- Usage();
-
- /* Parse the positional parameters. */
- Listname = av[0];
- Listaddr = av[1];
- Listadmin = av[2];
- Listhost = av[3];
- Article = av[4];
-
- /* Arrange for logging. */
- if (!Debugging && freopen(ERR_LOG, "a", stderr) == NULL)
- /* Sigh; error in error handler.... */
- (void)freopen("/dev/console", "w", stderr);
-
- if (Article && freopen(Article, "r", stdin) == NULL) {
- Fprintf(stderr, "%s: Can't open %s, %s.\n",
- Pname, Article, strerror(errno));
- exit(EX_NOINPUT);
- }
-
- /* Who are we? */
- #if defined(WHOAMI)
- Strcpy(Host, WHOAMI);
- #else
- if (gethostname(Host, sizeof Host) < 0) {
- Fprintf(stderr, "%s: Can't get hostname, %s.\n",
- Pname, strerror(errno));
- exit(EX_TEMPFAIL);
- }
- #endif /* defined(WHOAMI) */
-
- /* Read headers, storing the ones we want. */
- while (fgets(buff, sizeof buff, stdin)) {
- if ((p = IDX(buff, '\n')) != NULL)
- *p = '\0';
- else
- Fprintf(stderr, "%s: Header line too long (%d bytes max)\n\t%s\n",
- Pname, sizeof buff, buff);
- if (p == buff)
- /* Blank line means end of headers. */
- break;
-
- for (hp = Table; hp < ENDOF(Table); hp++)
- if (buff[hp->Length] == ':' && EQn(hp->Tag, buff, hp->Length)) {
- /* Skip whitespace. */
- for (p = &buff[hp->Length + 1]; *p && WHITE(*p); p++)
- continue;
- switch (hp->Flag) {
- case HDR_SUBJ:
- if (!EQn(p, "cmsg ", 5)) {
- Strcpy(hp->Value, p);
- break;
- }
- /* FALLTHROUGH */
- case HDR_CTRL:
- /* Eat rest of message. */
- while (fgets(buff, sizeof buff, stdin))
- continue;
- exit(EX_OK);
- /* NOTREACHED */
- case HDR_NORM:
- Strcpy(hp->Value, p);
- break;
- case HDR_FROM:
- #if defined(USE_PATH_FOR_FROM)
- /* Turn "joe@site.uucp (My Name)" into "(My Name)" */
- if ((p = IDX(p, ' ')) && p[1] == '(')
- Strcpy(Fullname, ++p);
- else
- Fullname[0] = '\0';
- #else
- Strcpy(hp->Value, p);
- #endif /* defined(USE_PATH_FOR_FROM) */
- break;
- case HDR_REFS:
- Strcpy(hp->Value, TrimReferences(p));
- break;
- #if defined(USE_PATH_FOR_FROM)
- case HDR_PATH:
- Strcpy(hp->Value, EditPath(p, Host));
- break;
- #endif /* defined(USE_PATH_FOR_FROM) */
- }
- }
- }
-
- /* Set up temp output. */
- if ((F = fopen(mktemp(strcpy(tmp, TEMPFILE)), "w")) == NULL) {
- Fprintf(stderr, "%s: Can't create %s, %s.\n", tmp, strerror(errno));
- exit(EX_CANTCREAT);
- }
-
- if (IDX(Listadmin, '@'))
- Strcpy(SenderAddr, Listadmin);
- else
- Sprintf(SenderAddr, "%s@%s", Listadmin, Listhost);
- if (IDX(Listaddr, '@'))
- Strcpy(ToAddr, Listaddr);
- else
- Sprintf(ToAddr, "%s@%s", Listaddr, Listhost);
-
- /* Print out a sanitized header for mail. */
- if (IDX(Listname, '@'))
- Strcpy(buff, Listname);
- else
- Sprintf(buff, "%s@%s", Listname, Listhost);
- Fprintf(F, "Received: from GATEWAY by %s with netnews\n", Host);
- Fprintf(F, "\tfor %s (%s)\n", ToAddr, buff);
- Fprintf(F, "To: %s\n", buff);
- for (hp = Table; hp < ENDOF(Table); hp++)
- if (hp->Flag == HDR_PATH) {
- #if defined(USE_PATH_FOR_FROM)
- Fprintf(F, "From: %s %s\n", hp->Value, Fullname);
- #endif /* defined(USE_PATH_FOR_FROM) */
- Fprintf(F, "Sender: %s\n", SenderAddr);
- }
- else if (hp->Value[0])
- Fprintf(F, "%s: %s\n", hp->Tag, hp->Value);
- Fprintf(F, "\n");
-
- /* Dump the body of the message. */
- while (fgets(buff, sizeof buff, stdin))
- Fputs(buff, F);
- if (fclose(F) == EOF)
- Fprintf(stderr, "%s: Error closing %s, %s.\n",
- Pname, tmp, strerror(errno));
-
- /* Set I/O correctly. Stderr is going to the log, stdin should be the
- * message we created. Easiest thing is to unlink an open file. */
- if (freopen(tmp, "r", stdin) == NULL) {
- Fprintf(stderr, "%s: Can't open %s for reading, %s.\n",
- Pname, tmp, strerror(errno));
- exit(EX_OSERR);
- }
- if (unlink(tmp) < 0)
- Fprintf(stderr, "%s: Can't unlink %s, %s.\n",
- Pname, tmp, strerror(errno));
-
- /* Common code for all argument vectors. */
- i = 0;
-
- #if defined(MAILSCRIPT)
- /* Build the MAILSCRIPT argument vector. */
- sv[i++] = MAILSCRIPT;
- /* Headers are inline. */
- sv[i++] = "-ASIS";
- /* Set the logical sender/from address. */
- sv[i++] = "-From";
- sv[i++] = SenderAddr;
- /* Set the recipient. */
- sv[i++] = "-recip";
- sv[i++] = ToAddr;
- #endif /* defined(MAILSCRIPT) */
-
- #if defined(SENDMAIL)
- /* Build of the SENDMAIL argument vector. */
- sv[i++] = SENDMAIL;
- /* Ignore periods as message terminator (same as -oi). */
- sv[i++] = "-i";
- /* Queued delivery. */
- sv[i++] = "-odq";
- /* Set the "From:" address. */
- sv[i++] = "-f";
- sv[i++] = SenderAddr;
- /* Set the recipient. */
- sv[i++] = ToAddr;
- #endif /* defined(SENDMAIL) */
-
- #if defined(MMDF)
- /* Build of the MMDF argument vector. */
- sv[i++] = MMDF;
- /* Deliver to mailbox (m), deliver all mail now (ln), trust me (t), send
- * no warnings (z), return to "Sender:" (s), get recipients from the
- * "To:" address (xto*). */
- #if defined(MMDF_DELIVER_NOW)
- sv[i++] = "-mlntzsxto*";
- #else
- sv[i++] = "-mtzsxto*";
- #endif /* defined(MMDF_DELIVER_NOW) */
- /* Set the From: address. */
- sv[i++] = SenderAddr;
- #endif /* defined(MMDF) */
-
- /* Null-terminate the vector. */
- sv[i] = NULL;
-
- if (Debugging) {
- for (i = 0; sv[i]; i++)
- (void)printf(" |%s| ", sv[i]);
- (void)printf("\n");
- if (HadEflag) {
- for (i = 0; sv[i]; i++)
- (void)printf(" [%s] ", sv[i]);
- (void)printf("\n");
- }
- while (fgets(buff, sizeof buff, stdin))
- Fputs(buff, stdout);
- exit(EX_OK);
- }
-
- /* Try to setuid, if desired. Could "factor out" the execv from the
- * #if, but I hate the way the resultant "dangling else" looks. */
- #if defined(TRUSTED)
- if (setuid(TRUSTED) < 0)
- Fprintf(stderr, "%s: Can't setuid to %d, %s.\n",
- Pname, TRUSTED, strerror(errno));
- else
- (void)execv(sv[0], sv);
- #else
- (void)execv(sv[0], sv);
- #endif /* defined(TRUSTED) */
-
- /* Something failed; dump the message and quit */
- Fprintf(stderr, "%s: Can't execv %s, %s.\n",
- Pname, sv[0], strerror(errno));
- while (fgets(buff, sizeof buff, stdin))
- Fputs(buff, stdout);
- exit(EX_OSERR);
- /* NOTREACHED */
- }
-